package com.lost.octopus.es.processor.service.common;

import com.baomidou.mybatisplus.annotation.TableId;
import com.sunsharing.share.common.base.exception.ShareBusinessException;
import com.sunsharing.share.common.base.exception.ShareResponseCode;
import com.sunsharing.share.common.mapper.BeanMapper;
import com.lost.octopus.es.processor.scheduler.RedisIndexRebuildRecorder;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

/**
 * 通用es服务实现
 * @author zhangbh
 **/
@Log4j2
public class BaseEsServiceImpl<E, I, V extends ElasticsearchRepository<E, I>> {

    @Autowired
    private RedisIndexRebuildRecorder redisIndexRebuildRecorder;

    @Autowired(required = false)
    private V esDao;

    public V getEsDao() {
        return this.esDao;
    }


    /**
     * 删除
     * @param id 主键
     */
    public void deleteById(I id) {
        getEsDao().deleteById(id);
    }

    /**
     * 批量删除
     * @param ids 主键
     */
    public void deleteAllById(Collection<I> ids) {
        getEsDao().deleteAllById(ids);
    }

    /**
     * 基础查询
     * @param ids 主键
     * @return 对象
     */
    public Iterable<E> findAllById(Iterable<I> ids) {
        Iterable<E> byIds = getEsDao().findAllById(ids);
        return Optional.of(byIds).orElse(null);
    }

    /**
     * 基础查询
     * @param id 主键
     * @return 对象
     */
    public E findById(I id) {
        Optional<E> byId = getEsDao().findById(id);
        return byId.orElse(null);
    }

    /**
     * 新增或者保存
     * @param entity 对象
     */
    public void saveOrUpdate(E entity) {
        if (entity == null) {
            throw new IllegalArgumentException("对象不能为空!");
        }

        // 判断索引是否在重建
        if (redisIndexRebuildRecorder.indexHasLocked(entity.getClass())) {
            throw new ShareBusinessException(ShareResponseCode.BIZ_ILLEGAL.getCode(), "索引正在重建，请稍后在保存数据");
        }

        Optional<E> byId = Optional.empty();
        try {
            byId = getEsDao().findById(getId(entity));
        } catch (Exception e) {
            log.error("当前索引不存在该id({})", getId(entity));
            log.error(e);
        }
        if (!byId.isPresent()) {
            getEsDao().save(entity);
            return;
        }
        E updateAddress = byId.get();
        // 更新的地址
        E newAddress = BeanMapper.mapNotNull(entity, updateAddress);
        getEsDao().save(newAddress);
    }

    public void batchSaveOrUpdate(List<E> list) {
        if (CollectionUtils.isEmpty(list)) {
            throw new IllegalArgumentException("对象集合不能为空!");
        }

        // 判断索引是否在重建
        if (redisIndexRebuildRecorder.indexHasLocked(list.get(0).getClass())) {
            throw new ShareBusinessException(ShareResponseCode.BIZ_ILLEGAL.getCode(), "索引正在重建，请稍后在保存数据");
        }

        List<E> saveOrUpdateList = new ArrayList<>(list.size());
        for (E entity : list) {
            Optional<E> byId = Optional.empty();
            try {
                byId = getEsDao().findById(getId(entity));
            } catch (Exception e) {
                log.error("当前索引不存在该id({})", getId(entity));
                log.error(e);
            }
            if (byId.isPresent()) {
                //设置更新属性
                E newAddress = BeanMapper.mapNotNull(entity, byId.get());
                saveOrUpdateList.add(newAddress);
            } else {
                saveOrUpdateList.add(entity);
            }
        }
        if (!CollectionUtils.isEmpty(saveOrUpdateList)) {
            getEsDao().saveAll(saveOrUpdateList);
        }
    }

    /**
     * 获取ID方法，目前还不知道怎么获取类的ID，简单实现，spring-data注解ID 或mybatis-plus
     * @param t 类型
     * @return 返回对象ID
     */
    protected I getId(E t) {
        for (Field field : t.getClass().getDeclaredFields()) {
            Id id1 = field.getAnnotation(Id.class);
            TableId id2 = field.getAnnotation(TableId.class);
            if (id1 != null || id2 != null) {
                ReflectionUtils.makeAccessible(field);
                // 相信注解
                return (I) ReflectionUtils.getField(field, t);
            }
        }
        log.error("实体{}获取不到id", t.getClass().getSimpleName());
        return null;
    }

}
